﻿using System;

namespace Snake.Gameplay
{
    public sealed class GameplayEffectSpec
    {
        public readonly GameplayEffect Effect;
        public GameplayEffectsContainer Container { get; set; }

        public GameplayAbilityComponent Owner => Container.Owner;

        public GameplayEffectSpec(GameplayEffect effect)
        {
            Effect = effect;
        }

        #region Runtime

        private float _elapse;

        private float _elapseTotal;

        public bool Invalid { get; set; }

        public bool IsInvalid
        {
            get
            {
                return Effect.durationPolicy switch
                {
                    DurationPolicy.Instant => true,
                    DurationPolicy.Infinite => false,
                    DurationPolicy.HasDuration => _elapseTotal >= Effect.durationMagnitude,
                    _ => throw new ArgumentOutOfRangeException()
                };
            }
        }

        #endregion

        public void OnAdd()
        {
            if (Effect.executePeriodicEffectOnApplication)
            {
                ApplyInstantAndPeriodic();
            }
            else
            {
                ApplyDurationAndInfinite();
            }

            foreach (var component in Effect.components)
            {
                component.Add(this);
            }

            AddGameplayCue();
        }

        public void OnRemove()
        {
            if (Effect.executePeriodicEffectOnApplication)
            {
                ApplyInstantAndPeriodic();
            }
            else
            {
                ResetDurationAndInfinite();
            }

            foreach (var component in Effect.components)
            {
                component.Remove(this);
            }

            RemoveGameplayCue();
        }

        /// <summary>
        /// 触发效果生效
        /// </summary>
        public void ApplyInstantAndPeriodic()
        {
            foreach (var modifier in Effect.modifiers)
            {
                modifier.ApplyInstantAndPeriodic(Container.Owner.AttributeSet);
            }

            foreach (var component in Effect.components)
            {
                component.Apply(this);
            }
        }

        public void ApplyDurationAndInfinite()
        {
            foreach (var modifier in Effect.modifiers)
            {
                modifier.ApplyDurationAndInfinite(Container.Owner.AttributeSet);
            }
        }

        public void ResetDurationAndInfinite()
        {
            foreach (var modifier in Effect.modifiers)
            {
                modifier.ResetDurationAndInfinite(Container.Owner.AttributeSet);
            }
        }

        public void OnUpdate(float dt)
        {
            if (IsInvalid)
            {
                return;
            }

            _elapse += dt;
            if (Effect.durationPolicy == DurationPolicy.HasDuration)
            {
                _elapseTotal += dt;
            }

            foreach (var component in Effect.components)
            {
                component.OnUpdate(this, dt);
            }

            //是否设置间隔，或者消耗时间小于间隔
            if (Effect.period <= 0f || _elapse < Effect.period)
            {
                return;
            }

            _elapse -= Effect.period;
            ApplyInstantAndPeriodic();
        }

        private void AddGameplayCue()
        {
            foreach (var tag in Effect.gameplayCues)
            {
                Owner.TagContainer.AddTag(tag);
            }
        }

        private void RemoveGameplayCue()
        {
            foreach (var tag in Effect.gameplayCues)
            {
                Owner.TagContainer.RemoveTag(tag);
            }
        }
    }
}